/**
 * 自动给预制和场景生成同名组件脚本，并自动写入属性，
 * 以下命名规则的节点会被写入脚本作为属性
 * $_name_type
 * 以$开头的节点，name表示节点的名字，type是要绑定的属性的类型，如Node，Label，Sprite等，
 * 且有效命名可以缩写，有效命名可以配置如：node，label，sprite，sp，bt等
 * 
 * 1.其中type可选，缺省时默认为该节点下面的第一个组件，没有组件时默认为节点类型 1
 * 2.如果已经有同名的脚本则不会生成，只会检查脚本里面的属性，只会补足新的属性，不会修改旧的属性 1
 * 3.分别统计生成的脚本，修改的脚本，错误的命名节点， 并打印，方便分析 1
 * 4.生成脚本数据类型，添加到DialogData.d.ts，方便GDialog调用时候的智能提示，只生成label和sprite对应的属性提示 1
 * 5.脚本类型名，数据类型名自动替换，包括setData里面的数据类型名 1
 * 6.同名属性检查，有同名属性会报错，并且不会生成或者修改ts脚本 1
 * 7.脚本自动添加到预制和场景，属性自动绑定，减少手动拖的步骤 1
 * 8.click_xx命名的节点，自动给该节点加上button组件，ts脚本生成click_xx回调函数，并绑定到button组件 1
 * 9.data_xx命名的节点，ts脚本自动加上监听玩家xx属性变化相关代码，并更新data_xx上面label显示，实现mvc模式 1
 * 
 */
let fs = require("fs");
/*******配置区***********/
/**工程目录 */
let ProjectPath = Editor.Project.path.replace(/\\/g, "\/");
/**查找的目标文件夹 */
let targetPath = ProjectPath + "/assets"; // "assets" 则为整个工程包
/**输出文件目录，null表示跟预制或者场景文件同一目录 */
let outPath = ProjectPath + "/assets";
/**数据信息文件DialogData.d.ts对应的目录 */
let DialogDataPath = `${ProjectPath}/DialogData.d.ts`;
/**组件模板，可以自定义ts组件模板，属性定义会添加到模板里面  */
let componentTemplate = fs.readFileSync(`${ProjectPath}/packages/tool_builder/lib/componentTemplate.ts`, "utf-8");
//组件和节点有效命名声明 type 声明，可扩展
let validType = {
    Node: ["node", "Node"],
    Sprite: ["sp", "Sprite", "sprite"],
    Label: ["label", "Label"],
    Button: ["bt", "Button", "button"],
    ScrollView: ["scrollView", "ScrollView"],
    Layout: ["layout", "Layout"],
    Widget: ["widget", "Widget"]
};

/*******实施区***********/
/**插入函数 */
function insertStr(soure, start, newStr) {
    return soure.slice(0, start) + newStr + soure.slice(start);
}
//cc._RF.push(module, 'de3d1tiSVFFJqr2bPRXpbAE', 'Game');
//cc._RF.push(module, 'af0aaDBbq9N9op1LUtEoHIi', 'Dialog2');
/**查找 library/imports 下面的所有js文件*/
function findJs(path, fileName, prefabPath) {
    fs.stat(path, (err, stats) => {
        if (err) {
            Editor.error(err.message || err);
        } else {
            if (stats.isFile()) {//文件
                console.log(' path ',path)
                if (path.endsWith(".js")) {
                    //读文件查找脚本id
                    let contentTs = fs.readFileSync(path, "utf-8");
                    let result = contentTs.match(new RegExp(`cc\\._RF\\.push\\(module, \\'([^,]*)\\', \\'${fileName}\\'`));
                    //检查脚本，然后写如数据
                    let content = fs.readFileSync(prefabPath, "utf-8");
                    let json = JSON.parse(content);
                    //脚本属性绑定
                    if (content.indexOf(result[1]) == -1) {
                        if (prefabPath.endsWith(".prefab")) {//预制
                            json.push(json[json.length - 1]);
                            json[json.length - 2] = {
                                "__type__": result[1],
                                "_name": fileName + ".ts",
                                "_objFlags": 0,
                                "node": {
                                    "__id__": 1
                                },
                                "_enabled": true,
                                "_id": ""
                            };
                            //遍历json，绑定属性和节点信息
                            for (let i = 0; i < json.length; i++) {
                                let info = json[i];
                                if (info._name) {
                                    if (info._name == fileName) {
                                        info._components.push({
                                            "__id__": json.length - 2
                                        });
                                        info._prefab.__id__++;
                                    }
                                    if (info._name.startsWith("$") || info._name.startsWith("data")) {
                                        //判断是否为node类型
                                        let isNode = false;
                                        let para = info._name.split("_");
                                        let type = para[2];
                                        if (validType.Node.indexOf(type) > -1) {//有显示声明类型
                                            isNode = true;
                                        } else if (info._components.length == 0) {//隐式，但是没有组件
                                            isNode = true;
                                        }
                                        let index = i;
                                        if (!isNode) {
                                            index = i + 1;
                                        }
                                        json[json.length - 2][info._name] = {
                                            "__id__": index
                                        }
                                    }
                                }

                            }
                            //写入原来的文件
                            fs.writeFileSync(prefabPath, JSON.stringify(json));
                        } else if (prefabPath.endsWith(".fire")) {//场景
                            json.push({
                                "__type__": result[1],
                                "_name": fileName + ".ts",
                                "_objFlags": 0,
                                "node": {
                                    "__id__": 2
                                },
                                "_enabled": true,
                                "_id": ""
                            });
                            json[2]._components.push({ "__id__": json.length - 1 });
                            //遍历json，绑定属性和节点信息
                            for (let i = 0; i < json.length; i++) {
                                let info = json[i];
                                if (info._name) {
                                    if (info._name.startsWith("$") || info._name.startsWith("data")) {
                                        //判断是否为node类型
                                        let isNode = false;
                                        let para = info._name.split("_");
                                        let type = para[2];
                                        if (validType.Node.indexOf(type) > -1) {//有显示声明类型
                                            isNode = true;
                                        } else if (info._components.length == 0) {//隐式，但是没有组件
                                            isNode = true;
                                        }
                                        let index = i;
                                        if (!isNode) {
                                            index = i + 1;
                                        }
                                        json[json.length - 1][info._name] = {
                                            "__id__": index
                                        }
                                    }
                                }
                            }
                            //写入原来的文件
                            fs.writeFileSync(prefabPath, JSON.stringify(json));
                            Editor.assetdb.refresh('db://assets', function (err, results) { });
                        }
                    }
                    //找到对应节点位置，插入button组件和事件
                    for (let i = json.length - 1; i >= 0; i--) {
                        let one = json[i];
                        let _name = one._name;
                        if (_name) {
                            let para = _name.split("_");
                            if (para[0] == "click") {
                                if (one._components.length > 0) {
                                    continue;
                                }
                                //遍历改变对应的id
                                for (let one of json) {//>i +2
                                    for (let key of Object.keys(one)) {
                                        let value = one[key];
                                        if (value) {
                                            if (Array.isArray(value)) {
                                                for (let id of value) {
                                                    if (typeof id == "object") {
                                                        if (typeof id.__id__ != "undefined" && id.__id__ > i) {
                                                            id.__id__ += 2;
                                                        }
                                                    }
                                                }
                                            } else if (typeof value == "object") {
                                                if (typeof value.__id__ != "undefined" && value.__id__ > i) {
                                                    value.__id__ += 2;
                                                }
                                            }
                                        }
                                    }
                                }
                                //插入button信息
                                one._components.push({
                                    "__id__": i + 1
                                });
                                let newJson = [...json.slice(0, i + 1)];
                                newJson.push({
                                    "__type__": "cc.Button",
                                    "_name": "",
                                    "_objFlags": 0,
                                    "node": {
                                        "__id__": i
                                    },
                                    "_enabled": true,
                                    "_normalMaterial": null,
                                    "_grayMaterial": null,
                                    "duration": 0.1,
                                    "zoomScale": 1.2,
                                    "clickEvents": [
                                        {
                                            "__id__": i + 2
                                        }
                                    ],
                                    "_N$interactable": true,
                                    "_N$enableAutoGrayEffect": false,
                                    "_N$transition": 3,
                                    "transition": 3,
                                    "_N$normalColor": {
                                        "__type__": "cc.Color",
                                        "r": 255,
                                        "g": 255,
                                        "b": 255,
                                        "a": 255
                                    },
                                    "_N$pressedColor": {
                                        "__type__": "cc.Color",
                                        "r": 211,
                                        "g": 211,
                                        "b": 211,
                                        "a": 255
                                    },
                                    "pressedColor": {
                                        "__type__": "cc.Color",
                                        "r": 211,
                                        "g": 211,
                                        "b": 211,
                                        "a": 255
                                    },
                                    "_N$hoverColor": {
                                        "__type__": "cc.Color",
                                        "r": 255,
                                        "g": 255,
                                        "b": 255,
                                        "a": 255
                                    },
                                    "hoverColor": {
                                        "__type__": "cc.Color",
                                        "r": 255,
                                        "g": 255,
                                        "b": 255,
                                        "a": 255
                                    },
                                    "_N$disabledColor": {
                                        "__type__": "cc.Color",
                                        "r": 124,
                                        "g": 124,
                                        "b": 124,
                                        "a": 255
                                    },
                                    "_N$normalSprite": null,
                                    "_N$pressedSprite": null,
                                    "pressedSprite": null,
                                    "_N$hoverSprite": null,
                                    "hoverSprite": null,
                                    "_N$disabledSprite": null,
                                    "_N$target": null,
                                    "_id": ""
                                });
                                let nodeIndex = 0;
                                if (prefabPath.endsWith(".prefab")) {//预制
                                    nodeIndex = 1;
                                } else if (prefabPath.endsWith(".fire")) {//场景
                                    nodeIndex = 2;
                                }
                                Editor.warn("_name", _name, i);
                                newJson.push({
                                    "__type__": "cc.ClickEvent",
                                    "target": {
                                        "__id__": nodeIndex
                                    },
                                    "component": "",
                                    "_componentId": result[1],
                                    "handler": _name,
                                    "customEventData": ""
                                });
                                newJson = newJson.concat(json.slice(i + 1));
                                json = newJson;
                                //写入原来的文件
                                fs.writeFileSync(prefabPath, JSON.stringify(json));
                                Editor.assetdb.refresh('db://assets', function (err, results) { });
                            }
                        }
                    }
                }
            } else {//文件夹
                fs.readdir(path, (err, content) => {
                    if (err) {
                        Editor.error(err.message || err);
                    } else {
                        for (let one of content) {
                            findJs(`${path}/${one}`, fileName, prefabPath);
                        }
                    }
                });
            }
        }
    })
}
//目前所有脚本，方便分析是否已经有同名脚本
let allTs = new Map();

//构建信息
//生成的脚本
let generateInfo = []; //{name: string, path: string}  脚本名字，路径
//修改的脚本
let modifyInfo = []; // {name: string, path: string, addProperty: string[]} 脚本名字，路径，添加的属性
//错误的节点或场景文件 出错的文件不会生成脚本，也不会修改已有的文件
/**
 * 错误信息包含：
 * 1.使用了没在validType里面找到的错误type
 * 2.有多个同名的属性
 */
let errorInfo = []; // {name: string, path: string, errorInfo: string} 脚本名字，路径，错误信息

/**
 * 处理ts脚本
 * @param {[]} json 预制或场景的json信息
 * @param {string} ts 要处理的ts代码内容
 * @param isNewTs 是否是新的ts脚本，方便输出构建信息
 * @param path 预制和场景文件的路径
 * @param fileName ts文件名字
 */
function handleTs(json, ts, isNewTs, path, fileName) {
    //生成的脚本信息
    let generate = null;
    //修改脚本信息
    let modify = { name: fileName, path: path, addProperty: [] };
    //错误信息
    let error = null;

    //节点名有效性判断
    let isValidType = true;
    //记录已经声明的属性，防止声明多个同名属性
    let AlreadyPro = [];
    //记录label和sprite属性，用来生成DialogData.d.ts智能提示
    let labelsprite = [];
    for (let i = json.length - 1; i >= 0; i--) {
        let one = json[i];
        let _name = one._name;
        if (_name) {
            /**参数分析
             * 0:$表示属性 
             * 1:节点名字
             * 2?:属性类型，节点或者组件，默认为第一个组件
             */
            let para = _name.split("_");
            if (para[0] == "$") {//1.属性，属性写入脚本，并自动绑定界面对应元素
                if (AlreadyPro.indexOf(_name) > -1) {//同名属性
                    isValidType = false;
                    error = { name: fileName, path: path, errorInfo: `有多个同名的属性，属性名：${_name}` };
                    errorInfo.push(error);
                    break;
                }
                AlreadyPro.push(_name);
                if (para[2]) {//有显示声明类型
                    let canFind = false;
                    for (let key of Object.keys(validType)) {
                        let value = validType[key];
                        if (value.indexOf(para[2]) > -1) {//节点名字正确声明的属性
                            if (key == "Label" || key == "Sprite") {
                                labelsprite.push(_name);
                            }
                            canFind = true;
                            if (ts.indexOf(_name) == -1) {//新的属性添加
                                let matchArr = ts.match(/extends[^{]*/);
                                let pos = matchArr[0].length + matchArr.index + 1;
                                let property = `\n\n\t@property(cc.${key})\n\t${_name}: cc.${key} = null;`;
                                ts = insertStr(ts, pos, property);
                                if (!isNewTs) {
                                    modify.addProperty.push(_name);
                                }
                            }
                            break;
                        }
                    }
                    if (!canFind) {
                        isValidType = false;
                        error = { name: fileName, path: path, errorInfo: `错误的组件和节点类型，节点名字：${_name}` };
                        errorInfo.push(error);
                        break;
                    }
                } else {
                    if (ts.indexOf(`${_name}:`) == -1) {//新的属性添加
                        let type = "cc.Node";
                        if (one._components.length > 0) {//有组件
                            //找到第一个组件
                            type = json[one._components[0].__id__].__type__;
                        }
                        if (type == "cc.Label" || type == "cc.Sprite") {
                            labelsprite.push(_name);
                        }
                        let matchArr = ts.match(/extends[^{]*/);
                        let pos = matchArr[0].length + matchArr.index + 1;
                        let property = `\n\n\t@property(${type})\n\t${_name}: ${type} = null;`;
                        ts = insertStr(ts, pos, property);
                        if (!isNewTs) {
                            modify.addProperty.push(_name);
                        }
                    }
                }
            } else if (para[0] == "click") {//2.点击的按钮，脚本自动写入按钮回调函数并绑定
                //ts脚本添加对应的同名回调函数
                if (ts.indexOf(_name) == -1) {//没有注册的函数
                    let index = ts.lastIndexOf("}");
                    let property = `\n\t${_name}() {\n\n\t}\n`;
                    ts = insertStr(ts, index - 1, property);
                }
            } else if (para[0] == "data") {//3.玩家数据变化自动更新界面相关显示，mvc模式
                if (ts.indexOf(_name) == -1) {//新的数据
                    let type = "";
                    if (one._components.length > 0) {//有组件
                        //找到第一个组件
                        type = json[one._components[0].__id__].__type__;
                    }
                    if (type == "cc.Label" || type == "cc.Sprite") {//只更新玩家文字和图片表现
                        //1.属性声明
                        let matchArr = ts.match(/extends[^{]*/);//类最上面
                        let pos1 = matchArr[0].length + matchArr.index + 1;
                        let property = `\n\n\t@property(${type})\n\t${_name}: ${type} = null;`;
                        ts = insertStr(ts, pos1, property);
                        //2.事件监听加入
                        let eventArr = ts.match(/GEvent\.onByArray\(\[/);//事件监听数组里面
                        let pos2 = eventArr[0].length + eventArr.index;
                        event = `\"${para[1]}\", `;
                        ts = insertStr(ts, pos2, event);
                        //3.事件监听的回调函数
                        if (type == "cc.Label") {
                            let pos3 = ts.search(/onLoad()|start()/m) - 4;//onLoad或者start上面
                            callBack = `\t[\"${para[1]}\"]() {\n\t\tthis.${_name}.string = GData.get(\"${para[1]}\").toString();\n\t}\n\n`;
                            ts = insertStr(ts, pos3, callBack);
                        } else {
                            let pos3 = ts.search(/onLoad()|start()/m) - 4;//onLoad或者start上面
                            callBack = 
`   
    [\"${para[1]}\"]() {
        cc.loader.loadRes(GData.get(\"${para[1]}\").toString(), cc.SpriteFrame, (err, sf: cc.SpriteFrame) => {
            if (err) {
                console.error(GData.get(\"${para[1]}\"), err.message || err);
            } else {
                this.${_name}.spriteFrame = sf;
            }
        })
    }

`;
                            ts = insertStr(ts, pos3, callBack);
                        }
                        //4.初始化显示调用
                        let initArr = ts.match(/initInterface\(\) {/);//initInterface函数里面
                        let pos4 = initArr[0].length + initArr.index;
                        init = `\n\t\tthis[\"${para[1]}\"]();`;
                        ts = insertStr(ts, pos4, init);
                        // if (!isNewTs) {
                        //     modify.addProperty.push(_name);
                        // }
                    } else {

                    }

                }
            }
        }
    }
    if (isValidType) {
        fs.writeFileSync(`${outPath}/${fileName}.ts`, ts);
        if (isNewTs) {
            generate = { name: fileName, path: outPath };
            generateInfo.push(generate);
        } else {
            if (modify.addProperty.length > 0) {
                modifyInfo.push(modify);
            }
        }
        //数据信息写入DialogData.d.ts
        let createDataType = function () {
            let newData = `type ${fileName + "Data"} = {\n`;
            for (let one of labelsprite) {
                newData += `\t${one}: string,\n`;
            }
            newData += "};\n\n";
            return newData;
        }
        let isExist = fs.existsSync(DialogDataPath);
        if (labelsprite.length > 0) {
            if (isExist) {
                let content = fs.readFileSync(DialogDataPath, "utf-8");
                let matchArr = content.match(new RegExp(`type ${fileName + "Data"} = {\n`));
                if (matchArr) {
                    let pos = matchArr[0].length + matchArr.index;
                    let newData = "";
                    for (let one of labelsprite) {
                        newData += `\t${one}: string,\n`;
                    }
                    content = insertStr(content, pos, newData);
                } else {
                    content += createDataType();
                }
                fs.writeFileSync(DialogDataPath, content);
            } else {//新加
                fs.writeFileSync(DialogDataPath, createDataType());
            }
        }
    }
    //查找import文件夹下面的js文件
    Editor.assetdb.refresh('db://assets', function (err, results) {
        findJs(ProjectPath + "/library/imports", fileName, path);
    });
}

//根据预制或者场景文件生成对应的ts
function outTs(path, fileName) {
    let content = fs.readFileSync(path, "utf-8");
    let json = JSON.parse(content);
    //标准模板
    let ts = componentTemplate;
    //更新名字
    ts = ts.replace(/componentTemplate/, fileName);
    ts = ts.replace(/any/g, fileName + "Data");

    let tsPath = allTs.get(fileName);
    if (tsPath) {//已经有脚本，只检查修改
        let ts = fs.readFileSync(tsPath, "utf-8");
        handleTs(json, ts, false, path, fileName);
    } else {//生成新脚本
        handleTs(json, ts, true, path, fileName);
    }
}
/**遍历要查找文件系统，找出所有.prefab预制和.fire场景资源  */
function readAll(path) {
    let stats = fs.statSync(path);
    if (stats.isFile()) {//文件
        if (path.endsWith(".prefab") || path.endsWith(".fire")) {
            Editor.log(path)
            let arr = path.split("/");
            let fileName = arr[arr.length - 1].split(".")[0];
            outTs(path, fileName);
        }
    } else {//文件夹
        let pathArr = fs.readdirSync(path);
        for (let one of pathArr) {
            readAll(`${path}/${one}`);
        }
    }
}
/**遍历找到assets目录下所有的ts文件 */
function findAllTs(path = ProjectPath + "/assets") {
    let stats = fs.statSync(path);
    if (stats.isFile()) {//文件
        if (path.endsWith(".ts")) {
            //保存所有ts文件
            allTs.set(path.match(/[^/]*.ts$/)[0].slice(0, -3), path);
        }
    } else {//文件夹
        let pathArr = fs.readdirSync(path);
        for (let one of pathArr) {
            findAllTs(`${path}/${one}`);
        }
    }
}

module.exports = {
    load() {
        // 当 package 被正确加载的时候执行
    },

    unload() {
        // 当 package 被正确卸载的时候执行
    },

    messages: {
        build() {
            Editor.log("开始生成/修改ts文件");
            findAllTs();
            readAll(targetPath);
            //输出构建信息
            if (errorInfo.length > 0) {
                Editor.error("错误信息:", errorInfo);
            }
            if (generateInfo.length > 0) {
                Editor.warn("生成脚本信息:", generateInfo);
            }
            if (modifyInfo.length > 0) {
                Editor.warn("修改脚本信息:", modifyInfo);
            }
            //更新一遍资源数据
            Editor.assetdb.refresh('db://assets', function (err, results) { });
        }
    },
};